<?php

namespace yunj;

use think\File;
use think\facade\Request;

final class YunjFile {

    private static $instance;

    public static function instance() {
        if (!self::$instance instanceof self) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Notes: 上传
     * Author: Uncle-L
     * Date: 2020/9/1
     * Time: 17:30
     * @param string $from
     * @return mixed
     */
    public function upload($from = 'input') {
        $fromConfig = self::fromConfig($from);
        $requestKey = $fromConfig['request_key'];
        $errorCallback = $fromConfig['error_callback'];
        $successCallback = $fromConfig['success_callback'];
        // 上传文件
        $file = request()->file($requestKey);
        if (!$file || !($file instanceof File)) return call_user_func_array($errorCallback, ['未找到上传的文件！文件大小可能超过php.ini默认2M限制']);
        $fileInfo = $file->getInfo();

        // 文件MIME类型
        $mime = $file->getMime();
        if ($mime == 'text/x-php' || $mime == 'text/html') return call_user_func_array($errorCallback, ['禁止上传php/html文件！']);

        // 格式、大小校验
        $fileConfig = yunj_config('file.');
        if ($file->checkExt($fileConfig['upload_img_ext'])) {
            $type = 'img';
            $imgSize = $fileConfig['upload_img_size'];
            if (!$file->checkSize($imgSize * 1024 * 1024)) return call_user_func_array($errorCallback, ["上传图片大小超过系统限制：{$imgSize}MB"]);
        } else if ($file->checkExt($fileConfig['upload_file_ext'])) {
            $type = 'file';
            $fileSize = $fileConfig['upload_file_size'];
            if (!$file->checkSize($fileSize * 1024 * 1024)) return call_user_func_array($errorCallback, ["上传文件大小超过系统限制：{$fileSize}MB"]);
        } else if ($file->checkExt($fileConfig['upload_media_ext'])) {
            $type = 'media';
            $mediaSize = $fileConfig['upload_media_size'];
            if (!$file->checkSize($mediaSize * 1024 * 1024)) return call_user_func_array($errorCallback, ["上传资源大小超过系统限制：{$mediaSize}MB"]);
        } else {
            return call_user_func_array($errorCallback, ['非系统允许的上传格式！']);
        }

        // 文件后缀
        $oldFileNameArr = explode('.', $fileInfo['name']);
        $ext = array_pop($oldFileNameArr);

        // 判断是否开启七牛云
        if (yunj_config('qiniu.is_enable', false)) {
            $fileName = md5(msectime() . rand_char(6));
            $fileName = date('Ymd') . '/' . $fileName . '.' . $ext;
            $key = $type . '/' . $fileName;
            $filePath = $fileFullPath = $fileInfo['tmp_name'];
            try {
                $fileUrl = Qiniu::instance()->upload($key, $filePath)->url();
            } catch (\Exception $e) {
                return call_user_func_array($errorCallback, [$e->getMessage()]);
            }
        } else {
            // 本地上传
            $res = $this->checkUploadDirExist();
            if ($res !== true) return call_user_func_array($errorCallback, [$res]);
            $filePath = '/upload/' . $type . '/';
            $fileFullPath = env('root_path') . 'public' . $filePath;
            // 上传
            $uploadFile = $file->move($fileFullPath);
            if ($uploadFile === false || (($fileName = $uploadFile->getSaveName()) && !is_file($fileFullPath . $fileName))) {
                return call_user_func_array($errorCallback, ['文件上传失败！' . ($uploadFile ? $uploadFile->getError() : $file->getError())]);
            }
            $fileUrl = Request::domain() . $filePath . str_replace("\\", "/", $fileName);
        }

        $data = [
            'file' => $file,
            'info' => $fileInfo,
            'type' => $type,
            'path' => isset($filePath) ? $filePath : '',
            'full_path' => isset($fileFullPath) ? $fileFullPath : '',
            'name' => isset($fileName) ? $fileName : '',
            'url' => isset($fileUrl) ? $fileUrl : '',
        ];
        return call_user_func_array($successCallback, [$data]);
    }

    /**
     * Notes: 校验[根目录\public\upload]是否存在
     * Author: Uncle-L
     * Date: 2021/11/10
     * Time: 14:11
     * @return bool|string
     */
    private function checkUploadDirExist() {
        $uploadDirPath = env('root_path') . "public/upload";
        if (is_dir($uploadDirPath)) {
            $isWriteable = dir_is_writeable($uploadDirPath);
            if (!$isWriteable) return "文件上传失败！请赋予文件夹[根目录\\public\\upload]及其子目录写入权限";
        } else
            try {
                mkdir($uploadDirPath, 0755, true);
            } catch (\Exception $e) {
                return "请创建文件夹[根目录\\public\\upload]，并赋予[upload]文件夹写入权限";
            }
        return true;
    }

    /**
     * Notes: 来源配置
     * Author: Uncle-L
     * Date: 2020/9/1
     * Time: 17:30
     * @param $from
     * @param string $code
     * @param null $def
     * @return array|mixed|null
     */
    private static function fromConfig($from, $code = '', $def = null) {
        $param = [
            // 上传文件的参数key
            'request_key' => 'file',
            // 错误返回回调
            'error_callback' => function ($msg = '') {
                return response_json(30001, $msg);
            },
            // 成功返回回调
            'success_callback' => function ($data = null) {
                return success_json($data);
            }
        ];
        switch ($from) {
            case 'input':
                $param = [
                        'error_callback' => function ($msg = '') {
                            return response_json(30001, $msg);
                        },
                        'success_callback' => function ($data = null) {
                            return success_json(['fileName' => $data['info']['name'], 'url' => $data['url']]);
                        }
                    ] + $param;
                break;
            case 'ckeditor':
                $param = [
                    'request_key' => 'upload',
                    'error_callback' => function ($msg = '') {
                        return json(['error' => ['message' => $msg, 'number' => 500]]);
                    },
                    'success_callback' => function ($data = null) {
                        return json(['fileName' => $data['info']['name'], 'uploaded' => 1, 'url' => $data['url']]);
                    }
                ];
                break;
            case "editormd":
                $param = [
                    'request_key' => 'editormd-image-file',
                    'error_callback' => function ($msg = '') {
                        return json(['success' => 0, "message" => $msg]);
                    },
                    'success_callback' => function ($data = null) {
                        return json(['success' => 1, "message" => "成功上传", 'url' => $data['url']]);
                    }
                ];
                break;
        }
        return !$code ? $param : (isset($param[$code]) ? $param[$code] : $def);
    }

}